#include "function_ality.hpp"

inline void cleanup_function_mess( string& s ){

	// APPROX: "over()" is too tricky a "case" to
	// handle unless the sub-query aliases issue is
	// fixed (in which case this one can be very
	// conveniently - and immediately - accommodated).
	static const string fnames_ar = "(not_in|over)";
	// APPROX: "format()" is a lost case - too
	// much unpredictable junk - hence ignored.
	static const string fnames_gr = "(encode|format|to_hex|using"
			"|encoding|((n)?varchar))";
	static const string fnames_gs = "(upper|lower|initcap|md5"
			"|avg|((bit|bool)_(and|or))|count|every|max|min|sum"
			"|((bit|octet|(char(acter)?))_length)|ascii|(quote_[^\\)]+)"
			"|age|isfinite|(justify_[^\\)]+)|istrue"
			"|sign|abs|mod|(ceil(ing)?)|floor|int4and|instr"
			"|(concat(_ws)?)"
			"|distinct)"; // Not a function (strictly), still useful.
	static const string fnames_gfc = "(round|(l|r)pad|substr|nvl|coalesce"
			"|(l|r)trim|to_char|to_date|to_timestamp|to_number|btrim"
			"|(convert(_(from|to))?)|decode|length|left|repeat|replace"
			"|reverse|right|split_part|strpos|to_ascii|translate|(regexp_[^\\)]+)"
			"|ADD_MONTHS|NEXT_DAY" // Have other stuff...
			"|LAST_DAY)"; // Shouldn't have other stuff...
	// ANOMALY: "substring()" not in "fnames_gfw" according
	// to the (mostly Oracle-style) usage in the sample log.
	static const string fnames_gfc_part = "(substring)";
	static const string fnames_glc = "(date_part|date_trunc)";
	static const string fnames_gfw = "(cast)";
	static const string fnames_glw = "(extract|trim|position)";

	static const string begin = "\\b(?<!')(", space = "\\s*", end = "\\(.*)";

	// Removes (SAFELY) "fn(~)" or replaces it with "~"" or..
	static const RE atypical_remove( begin+fnames_ar+space+end, icase );
	static const RE generic_remove( begin+fnames_gr+space+end, icase );
	static const RE generic_subst( begin+fnames_gs+space+end, icase );
	// "picks" the first "part" before the comma (if any) or..
	static const RE generic_fcpick( begin+fnames_gfc+space+end, icase );
	static const RE generic_fcpick_part( begin+fnames_gfc_part+space+end, icase );
	static const RE generic_lcpick( begin+fnames_glc+space+end, icase );
	// "picks" the first (or last) word in the "parameter" part.
	static const RE generic_fwpick( begin+fnames_gfw+space+end, icase );
	// EXAMPLE: p.q.r from "fn(abc...xyz p.q.r)" where "fn"
	// must be in fnames_gl (CONVENTION: gl ~ Generic Last).
	static const RE generic_lwpick( begin+fnames_glw+space+end, icase );

	recursive_fn_transform( s, atypical_remove, 3 );
	recursive_fn_transform( s, generic_remove, 0 );
	recursive_fn_transform( s, generic_subst, 1 );
	recursive_fn_transform( s, generic_fcpick, 2 );
	recursive_fn_transform( s, generic_fcpick_part, 9 );
	recursive_fn_transform( s, generic_lcpick, 5 );
	recursive_fn_transform( s, generic_fwpick, 4 );
	recursive_fn_transform( s, generic_lwpick, 7 );

}

inline void make_parsing_ready( string&s ){

	static const regex whitespace( "\\s+" );

	static const regex empty_function( "\\b(?<!')\\w+\\((\\s|\\d)*\\)", regex::icase );

	static const regex external( "from external '[^']+'(\\s+\\([^\\(\\)]+\\))?", regex::icase );

	static const string d1 = "(LEFT|FULL|RIGHT)", d2 = "(OUTER|INNER)";
	static const regex concat_joins( "\\b(("+d1+"\\s+)?("+d2+"\\s+)?JOIN)\\b", regex::icase );
	static const regex group_by( "\\b(GROUP)\\s+(BY)\\b", regex::icase );
	static const regex order_by( "\\b(ORDER)\\s+(BY)\\b", regex::icase );
	static const regex partition_by( "\\b(PARTITION)\\s+(BY)\\b", regex::icase );

	static const regex dor_hack( "\\b(distribute on random)\\s*", regex::icase );

	static const RE expand_rest( "\\b(?<!')([_a-zA-Z]\\w*\\(.+)", icase ); // RESOLVE

	// Removes "[NOT] LIKE NON-SPACE".
	static const regex like_unlike( "\\b(NOT\\s+)?(LIKE)\b\\s*[^,\\s]+", regex::icase );
	static const regex casting_colon( "::[^,\\s]+", regex::icase );
	static const regex is_not_null( "\\b(IS)\\s+(NOT\\s+)?(NULL)", regex::icase );

	static const regex open_parenthesis( "\\(\\s+", regex::icase );
	static const regex close_parenthesis( "\\s+\\)", regex::icase );

	s = regex_replace( s, concat_joins, "J#O#I#N " );
	s = regex_replace( s, group_by, "GR#OUP#BY " );
	s = regex_replace( s, order_by, "OR#DER#BY " );
	s = regex_replace( s, partition_by, "PA#RTI#T#ION#BY " );

	s = regex_replace( s, dor_hack, " " );

	for( string temp = s;
		 s != ( temp = regex_replace( temp, empty_function, "E#M#P#T#Y" ) );
		 s = temp );

	cleanup_function_mess( s );

	s = regex_replace( s, external, "FROM E#X#T#E#R#N#A#L " );
	{ trim( s ); s = regex_replace( s, whitespace, " " ); }

	recursive_fn_transform( s, expand_rest, 8 );

	s = regex_replace( s, like_unlike, "" );
	s = regex_replace( s, casting_colon, "" );
	s = regex_replace( s, is_not_null, "" );

	s = regex_replace( s, open_parenthesis, "(" );
	s = regex_replace( s, close_parenthesis, ")" );

}
